const net = require('net');
const EventEmitter = require('events');
const s7protoHead_COTP = Buffer.from(
  "0300001611E00000000100C0010AC1020100C2020301",
  "hex"
);
const s7protoHead_setupcommunication = Buffer.from(
  "0300001902f08032010000000000080000f0000001000101e0",
  "hex"
);
const s7protoHead_readvar = Buffer.from(
  "0300002B02f080320100000100001a00000402120a10020001000184000000120a10040004000006000080",
  "hex"
);
class S7 extends EventEmitter {
  constructor(ip='192.168.1.2',reconnectInterval=30000) {
    super();
    this.ip = ip;
    this.port = 102;
    this.reconnectInterval = reconnectInterval;
    this.lastDinputs = -1;
    this.pointer = -1;
    this.plcDinputs = -1;
    this.lastDI = -1;
    this.lastDIindex = -1;
    this.lastPointerTime = null; // 添加上一次pointer更新的时间戳
    this.timeThreshold = 210;  // 设置时间间隔阈值
    this.readvarCount = 0;  // 记录case 0xf0的进入次数

    console.log(`S7 Adress: ${this.ip}`);
    this.connect();
  }

  connect() {
    // 确保在建立新连接前销毁旧连接
    if (this.client) {
      try {
        this.client.destroy();
        this.client = null;
      } catch (err) {
        console.error(`S7 Error: ${err.message}`);
      }
    }

    // 使用createConnection创建新的连接
    const client = net.createConnection({host: this.ip,port: this.port}, () => {
      // console.log(`S7 driver: connected to device at ${this.ip}:${this.port}`);
      this.client = client;
      client.write(s7protoHead_COTP);
      this.lastReadVarTime = performance.now();
      this.maxReadVarInterval = 0;

    });

    // 监听错误事件
    client.on("error", () => {
      this.reconnect();
    });

    // 监听结束事件
    client.on("end", () => {
      this.reconnect();
    });
    // 处理数据
    client.on("data", (data) => {
      this.handleData(data);
    });
  }

  // 处理数据
  handleData(data) {
    if (data.length > 5) {
      const pduType = data.readUInt8(5);
      switch (pduType) {
        case 0xd0: // PDU: CC 连接确认
          this.emit('connected');
          this.client.write(s7protoHead_setupcommunication);
          this.ReadVarRoundTime = 0;
          break;
        case 0xf0: // PDU 类型: DT 数据
          this.readvarCount++;
          this.maxReadVarInterval = Math.max(this.maxReadVarInterval, performance.now() - this.lastReadVarTime);
          this.lastReadVarTime = performance.now();
          setTimeout(() => {
            this.ReadVarRoundStartTime = performance.now();
            this.client.write(s7protoHead_readvar);
          }, 5);
          if (data.length < 20) {
            console.error(`S7 Error: invalid data length: ${data.length}`);
            this.reconnect();
            break;
          }
          // 去掉bytes前7个字节(TPKT和COTP)
          let bytes = data.slice(7);
          // 去掉12个字节（S7协议头）
          bytes = bytes.slice(12);
          const s7ParameterFunction = bytes.readUInt8(0);
          if (s7ParameterFunction === 0x04) {
            this.ReadVarRoundTime = Math.max(performance.now() - this.ReadVarRoundStartTime, this.ReadVarRoundTime);
            // 去掉2个字节（S7 parameter)
            bytes = bytes.slice(2);
            // const bytes = data.slice(data.length - 12);
            const plcDinputs = bytes.readUInt8(4);
            if (plcDinputs != this.lastDinputs) {
                  const AI0 = bytes.readUInt16BE(10);
                  const AI1 = bytes.readUInt16BE(12);
                  const AI2 = bytes.readUInt16BE(14);
                  const AI3 = bytes.readUInt16BE(16);
                  const data = [AI0, AI1, AI2, AI3];
                  const result = {
                    data,
                    ReadVarTimes: this.readvarCount,
                    DIx: plcDinputs,
                    DIxLast: this.lastDinputs,
                  };
                  this.emit('newpointer', result);
                  const currentTime = Date.now();
                  if (!this.lastPointerTime) {
                    this.lastPointerTime = currentTime;
                  }
                  const interval = currentTime - this.lastPointerTime;
                  if (interval > this.timeThreshold) {
                    console.table({
                      PointerInterval: interval,
                      ReadVarTimes: this.readvarCount,
                      MaxReadVarInterval: this.maxReadVarInterval,
                      AverageReadVarInterval: interval / this.readvarCount,
                      MaxReadVarRoundTime: this.ReadVarRoundTime,
                      DIx: plcDinputs,
                      DIxLast: this.lastDinputs,
                    });
                    }
                  this.lastDinputs = plcDinputs;
                  this.readvarCount = 0;
                  this.lastPointerTime = currentTime;
                  this.maxReadVarInterval = 0;
                
            }
            }
            break;
      }
    } else {
      console.error('S7 driver: data length is less than 5');
      this.reconnect();
    }
  }


  reconnect() {
    // console.log(`S7 driver: attempting to reconnect to device at ${this.ip}:${this.port}...`);
    this.emit('reconnect');
    setTimeout(() => {
      this.connect();
    }, this.reconnectInterval);
  }

}
/**
 * 检查数字第几个位是否为0
 * @param {number} num 要检查的数字
 * @param {number} bitPosition 要检查的位的位置（从0开始）
 * @returns boolean
 */
function isBitZero(num, bitPosition) {
  // 创建掩码，将1左移到我们想检查的位
  const mask = 1 << bitPosition;
  // 按位与操作，检查该位是否为0
  return (num & mask) === 0;
}
/**
 * 返回期望的下一个DI
 * @param {*} lastDI 上一个DI
 * @param {*} plus 增加还是减少，即光电变化是0->4还是4->0
 * @returns Int
 */
function getExpectedNewDI(lastDI, plus = true) {
  let expectedNewDI;
  if (plus) {
    expectedNewDI = lastDI + 1;
    if (expectedNewDI === 5) {
      expectedNewDI = 0;
    }
  } else {
    expectedNewDI = lastDI - 1;
    if (expectedNewDI === -1) {
      expectedNewDI = 4;
    }
  }
  return expectedNewDI;
}
module.exports = S7;

if (require.main === module) {
  const s7 = new S7('192.168.1.2');
  s7.on('connected', () => {
    console.log('S7 driver: connected');
  });
  let count = 0;
  s7.on('newpointer', (result) => {
    count++;
    s7.pointer += 1;
    // console.table(result);
    if (s7.pointer%100 === 0) {
      const time = new Date();
      console.log(`${time.toLocaleString()}: pointer=${s7.pointer}, total events=${count}`);
    }
    const { data, ReadVarTimes, DIx, DIxLast } = result;
    const diff = DIx - DIxLast;
    if (![1,-4].includes(diff)) {
      console.table({DIx, DIxLast, ReadVarTimes})
    }
    for (let i = 0; i < 4; i++) {
      if (data[i] > 22000) {
        console.log(`AI${i}=${data[i]}:Pointer=${this.pointer}`); 
      } 
    }
  });
}